package sms.file.store;

import sms.model.BaseEntity;
import sms.persistence.IPersistenceDao;
import sms.persistence.PersistenceException;

import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

public class PersistenceDaoImpl implements IPersistenceDao {
    private final Path dataPath = Paths.get(".", "data");

    @Override
    public <T extends BaseEntity> List<T> list(Class<T> type) throws PersistenceException {
        List<T> results = new LinkedList<>();
        //获取所有以.bin结尾的文件路径流
        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(this.getEntitiesPath(type), "*.bin")) {
            //遍历所有以.bin结尾的文件路径
            for (Path path : directoryStream) {
                //将文件读取为实体对象添加至集合中
                results.add(this.readEntity(path));
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new PersistenceException(e);
        }
        return results;
    }

    @Override
    public <T extends BaseEntity> Optional<T> get(Class<T> type, Serializable id) throws PersistenceException {
        try {
            return Optional.ofNullable(this.readEntity(this.getEntityPath(type, id)));
        } catch (IOException | ClassNotFoundException e) {
            throw new PersistenceException(e);
        }
    }

    @Override
    public <T extends BaseEntity> String save(T entity) throws PersistenceException {
        try {
            return this.saveEntity(entity);
        } catch (IOException e) {
            throw new PersistenceException(e);
        }
    }

    @Override
    public <T extends BaseEntity> String delete(Class<T> type, Serializable id) throws PersistenceException {
        try {
            return this.deleteEntity(type, id);
        } catch (IOException e) {
            throw new PersistenceException(e);
        }
    }

    /**
     * 根据实体类名作为文件目录名称
     *
     * @param type
     * @return
     */
    private Path getEntitiesPath(Class<?> type) {
        Path path = dataPath.resolve(type.getSimpleName());
        File file = path.toFile();
        if (!file.exists()) {
            file.mkdirs();
        }
        return path;
    }

    /**
     * 每个实体存储文件路径为：实体名+id.bin
     *
     * @param type
     * @param id
     * @return
     */
    private Path getEntityPath(Class<?> type, Serializable id) {
        return getEntitiesPath(type).resolve(String.format("%s.bin", id));
    }

    /**
     * 从文件中读取实体对象
     *
     * @param path
     * @param <T>
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private <T extends BaseEntity> T readEntity(Path path) throws IOException, ClassNotFoundException {
        try (ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(path))) {
            return (T) objectInputStream.readObject();
        }
    }

    /**
     * 将实体对象保存至文件中
     *
     * @param t
     * @param <T>
     * @throws IOException
     */
    private <T extends BaseEntity> String saveEntity(T t) throws IOException {
        Path path = this.getEntityPath(t.getClass(), t.getId());
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(path))) {
            objectOutputStream.writeObject(t);
        }
        return path.toFile().getAbsolutePath();
    }

    /**
     * 删除指定对象对应的文件
     *
     * @param type
     * @param id
     * @throws IOException
     */
    private String deleteEntity(Class<?> type, Serializable id) throws IOException {
        Path path = this.getEntityPath(type, id);
        Files.deleteIfExists(path);
        return path.toFile().getAbsolutePath();
    }
}
